Mass Assignment脆弱性
外部から入力された値をフィルタせずにそのまま入力値とすることで意図しないプロパティが外部から設定できてしまう問題。
読みは「マスアサインメントぜいじゃくせい」。
脆弱性のあるコード例
たとえば以下のようなUserクラスを考える
mail_authorizedはメール認証
code:User.php
class User
{
protected function __construct(
private ?UserId $id = null,
private ?string $name = null,
private ?bool $mail_authorized
) {}
public static function createFromArray(array $params): self
{
return new self(
id: isset($params'id') ? new UserId($params'id') : null, );
}
public static function findById(int $id): ?self
{
if (empty($row) {
return null;
}
return static::createFromArray($row);
}
public function completeMailAuthorize(): void
{
$this->mail_authorized = true;
}
public function save(): bool
{
return db_upsert('users', $this->params) !== null;
}
}
User::findById()でデータベースから検索して取得できる
たとえばメール認証するページはこう
code:mail_authorize.php
<?php session_start();
$session_user_id = ($_SESSION'user_id' ?? null; if ($session_user_id === null) {
throw new ForbiddenException();
}
assert_access_token();
$user = User::findById($session_user_id) or throw new ForbiddenException();
$token = filter_var($_GET'token' ?? '', FILTER_DEFAULT); if (!check_mail_authorization($user, $token)) {
throw new ForbiddenException();
}
$this->completeMailAuthorize();
$user->save();
外部から値を受け取って保存しようとする雑なコード
code:update_user_profile.php
<?php session_start();
$session_user_id = ($_SESSION'user_id' ?? null; if ($session_user_id === null) {
throw new ForbiddenException();
}
assert_access_token();
$user->save();
ここで$_POSTをそのままUser::createFromArray()に渡しちゃってるのがまずい
['id' => $session_user_id]で最低限IDの上書きできないようにしているが、これでは足りない
それすらしないと好き勝手に他のユーザーのアカウント情報を変更できてしまう
対策
フレームワークによって対策が異なる。
一般的な対策
入力値をきちんとチェックして入力する
code:php
assert_access_token();
$name = filter_var($_GET'name' ?? false); if ($name === '' || $name === false) {
throw new BadRequestException();
}
$user = User::createFromArray([
'id' => $session_user_id,
'name' => $name,
]);
$user->save();
Laravel
フォームリクエストとModelの$fillableを併用するといいんじゃないでしょうか
Webを検索すると$fillableよりも$guardedの方が簡単! という記事も出てきますが、セキュアコーディングの観点からは保存していいカラムを明示的に指定する方が事故りにくいのではないでしょうか 思考停止して安易に$fillableに追加すると運用が常態化するとまずいのは同じですけど…
頻繁にカラムの加除が発生しないような設計を心掛けたいものです
CakePHP
エンティティークラスの$_accesibleを使うといいんじゃないでしょうか
あとSecurityコンポーネントが有効な場合はFormヘルパーを使っているとフォーム改竄検知の恩恵を受けられます